﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using DryIoc;
using NModbus;
using NModbus.Data;
using NModbus.Device;
using NModbus.Extensions.Enron;
using NModbus.Serial;
using WheelClean2.Model;
using static OpenCvSharp.Stitcher;

namespace WheelClean2.Mould
{

    public static class ModbusTCP
    {
        private static  TcpClient _tcpClient;
        // 创建modbus
        private static ModbusFactory _factory ;
        private static IModbusMaster _modbusMaster;
        private static string _ipAddress;
        private static int _port;
        private static byte _slaveId;

        private static string DeviceStatus;

        /// <summary>
        /// 线程控制
        /// </summary>
        private static CancellationTokenSource _cts;
        private static readonly object _syncLock = new object();

        public static bool IsRunning { get; private set; }

        public static List<PLCInfos> PLCInfosl;

        public static void Init(string ipAddress, int port = 502, byte slaveId = 1)
        {
            _cts = new CancellationTokenSource();
            _ipAddress = ipAddress;
            _port = port;
            _slaveId = slaveId;
            _factory = new ModbusFactory();
        }

        public static void Connect()
        {
            Thread thread = new Thread(() =>
            {
                bool result = true;
                while(result)
                {
                    try
                    {
                        _tcpClient = new TcpClient(_ipAddress, _port);
                        _modbusMaster = _factory.CreateMaster(_tcpClient);
                     
                        result = false;


                    }
                    catch (Exception ex)
                    {
                        result = true;
                        Console.WriteLine($"连接失败: {ex.Message}");
                    }
                    Thread.Sleep(2000);

                }

        


            });
            thread.Start();


        }

        public static void Disconnect()
        {
            _tcpClient?.Close();
        }


        /// <summary>
        /// 获取PLC设备列表
        /// </summary>
        public static void GetPLCDevices()
        {
            PLCInfosl = new List<PLCInfos>();
            //获取所有设备
            using (var db = new AppDbContext())
            {
                PLCInfosl.AddRange(db.PLCInfos.ToList());
            }
            //数据赋值
            PLCInfosl.ForEach(t => {
                if(!t.Operate.Contains("W"))
                {
                    t.IsEnabled = false;
                }
                else
                {
                    t.IsEnabled = true;
                }
            });
            

            //获取所有线圈设备
            List<PLCInfos> Coils = PLCInfosl.Where(t => t.Address.Contains("0X")).ToList();
            //排序
            Coils.ForEach(t =>
            {
                t.AddressSort = Convert.ToInt16(t.Address.Replace("0X", ""));
            });
            //连续地址的归为一组
            var groups = GroupConsecutiveNumbersEfficient(Coils);
            //对每组进行PLC读值
            groups.ForEach(t =>
            {
                Task.Run(() => CollectCoilsData(_cts.Token,t));

            });

            //获取所有寄存器设备
            List<PLCInfos> Holding = PLCInfosl.Where(t => !t.Address.Contains("0X")&& !t.Address.Contains("4X")).ToList();
            //排序
            Holding.ForEach(t =>
            {
                t.AddressSort = Convert.ToInt16(t.Address.Replace("4X", ""));
            });
            //连续地址的归为一组
            var groups2 = GroupConsecutiveNumbersEfficient(Holding,2);
            //对每组进行PLC读值
            groups2.ForEach(t =>
            {
                Task.Run(() => CollectHoldingData(_cts.Token, t));

            });
        }

        /// <summary>
        /// 获取线圈数据
        /// </summary>
        /// <param name="token"></param>
        /// <param name="plcInfos"></param>
        private static void CollectCoilsData(CancellationToken token,List<PLCInfos> plcInfos)
        {
            while (!token.IsCancellationRequested&& _modbusMaster!=null)
            {
                // 模拟数据采集
                Thread.Sleep(100);
                var result = ReadCoils((ushort)plcInfos[0].AddressSort, (ushort)plcInfos.Count);
                if (result == null)
                    return;
                for (int i = 0; i < result.Count(); i++)
                {
                    plcInfos[i].Data = result[i].ToString();
                }
            }
        }

        /// <summary>
        /// 获取寄存器数据
        /// </summary>
        /// <param name="token"></param>
        /// <param name="plcInfos"></param>
        private static void CollectHoldingData(CancellationToken token, List<PLCInfos> plcInfos)
        {
            while (!token.IsCancellationRequested && _modbusMaster != null)
            {
                // 模拟数据采集
                Thread.Sleep(100);
                var result = ReadHoldingRegisters((ushort)plcInfos[0].AddressSort, (ushort)(plcInfos.Count * 2));
                for (int i = 0; i < result.Count(); i=i+2)
                {
                    if(plcInfos[i/2].DataType.Contains("INT"))
                    {
                        plcInfos[i / 2].Data = result[i].ToString();
                    }
                    else
                    {
                        ushort[] data= new ushort[] { result[i], result[i + 1] };
                        plcInfos[i / 2].Data = ChangeFloat(data).ToString();
                    }
                  
                }
            }
        }
        public  static void StopCollection()
        {
            _cts?.Cancel();
        }

        public static List<List<PLCInfos>> GroupConsecutiveNumbersEfficient(List<PLCInfos> numbers,int interval=1)
        {
            if (numbers == null || numbers.Count == 0)
                return new List<List<PLCInfos>>();

            numbers = numbers.OrderBy(t => t.AddressSort).ToList();
            var result = new List<List<PLCInfos>>();
            int start = 0;

            for (int i = 1; i < numbers.Count; i++)
            {
                if (numbers[i].AddressSort != numbers[i - 1].AddressSort + interval)
                {
                    result.Add(new List<PLCInfos>(numbers.Skip(start).Take(i - start)));
                    start = i;
                }
            }

            result.Add(new List<PLCInfos>(numbers.Skip(start)));
            return result;
        }


        /// <summary>
        /// 数据处理
        /// </summary>
        public static void DataHandling()
        {
            if (PLCInfosl == null || PLCInfosl.Count == 0)
                return;
            //报警信息处理
            GetWarningInfo();

        }


        /// <summary>
        /// 获取报警信息
        /// </summary>
        public static void GetWarningInfo()
        {

            //获取设备状态      
            var status = PLCInfosl.FirstOrDefault(t => t.Address.Contains("24398"));
            if(status!=null)
            {
                DeviceStatus=Enum.GetName(typeof(DeviceStatusEnum), status.Data) ;
                //处于故障状态
                if(Convert.ToInt16(status.Data) ==9)
                {
                    //获取故障码
                    var warning = PLCInfosl.FirstOrDefault(t => t.Address.Contains("24388"));
                    if (warning != null)
                    {

                    }

                }
            }

        }



        /// <summary>
        /// 
        /// 
        /// 下发轨迹信息
        /// </summary>
        /// <param name="tracks">轨迹信息</param>
        public static void SendTraceInfos(List<Track> tracks)
        {
            ushort begin = 42288;
            List<ushort> ushorts =new List<ushort>();
            tracks.ForEach(t =>
            {
                //序号
                ushorts.Add((ushort)t.Number);
                //功能码
                ushorts.Add((ushort)(t.FunctionCode ?? 0));
                //私有数据1
                ushorts.AddRange(GetFloat(t.Data1 ?? 0));
                //私有数据2
                ushorts.AddRange(GetFloat(t.Data2 ?? 0));
                //公共数据
                ushorts.AddRange(GetFloat((float)(t.Time ?? 0)));

            });
            // 按每组5个元素进行分组
            int groupSize = 120;
            var grouped = ushorts.Select((value, index) => new { value, index })
                                .GroupBy(x => x.index / groupSize)
                                .Select(g => g.Select(x => x.value).ToList())
                                .ToList();
            //要对程序进行分段处理 120为一组

            grouped.ForEach(t =>
            {
                WriteMultipleRegisters(begin, t.ToArray());
                begin += (ushort)t.Count;
            });



        }



        // 读取保持寄存器
        public static ushort[] ReadHoldingRegisters(ushort startAddress, ushort numberOfPoints)
        {
            try
            {
                return _modbusMaster.ReadHoldingRegisters(_slaveId, startAddress, numberOfPoints);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"读取保持寄存器失败: {ex.Message}");
                return null;
            }
        }

        // 写入单个寄存器
        public static bool WriteSingleRegister(ushort registerAddress, ushort value)
        {
            try
            {
                _modbusMaster.WriteSingleRegister(_slaveId, registerAddress, value);
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"写入寄存器失败: {ex.Message}");
                return false;
            }
        }

        // 写入多个寄存器
        public static bool WriteMultipleRegisters(ushort startAddress, ushort[] data)
        {
            try
            {
                _modbusMaster.WriteMultipleRegisters(_slaveId, startAddress, data);
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"写入多个寄存器失败: {ex.Message}");
                return false;
            }
        }

        // 读取线圈状态
        public static bool[] ReadCoils(ushort startAddress, ushort numberOfPoints)
        {
            try
            {
                return _modbusMaster.ReadCoils(_slaveId, startAddress, numberOfPoints);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"读取线圈状态失败: {ex.Message}");
                return null;
            }
        }

        // 写入单个线圈
        public static bool WriteSingleCoil(ushort coilAddress, bool value)
        {
            try
            {
                _modbusMaster.WriteSingleCoil(_slaveId, coilAddress, value);
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"写入线圈失败: {ex.Message}");
                return false;
            }
        }


        public static void WriteFloat( ushort startAddress, float value, bool swapRegisters = false)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            ushort[] registers = new ushort[2];

            if (swapRegisters)
            {
                // 字节顺序 ABCD → CDAB
                registers[0] = BitConverter.ToUInt16(bytes, 2);
                registers[1] = BitConverter.ToUInt16(bytes, 0);
            }
            else
            {
                registers[0] = BitConverter.ToUInt16(bytes, 0);
                registers[1] = BitConverter.ToUInt16(bytes, 2);
            }

            WriteMultipleRegisters(startAddress, registers);
        }

        /// <summary>
        /// 将数字转成float
        /// </summary>
        /// <param name="value"></param>
        /// <param name="swapRegisters"></param>
        /// <returns></returns>
        public static ushort[] GetFloat( float value, bool swapRegisters = false)
        {
            //byte[] bytes = BitConverter.GetBytes(value);
            //ushort[] registers = new ushort[2];

            //if (swapRegisters)
            //{
            //    // 字节顺序 ABCD → CDAB
            //    registers[0] = BitConverter.ToUInt16(bytes, 2);
            //    registers[1] = BitConverter.ToUInt16(bytes, 0);
            //}
            //else
            //{
            //    registers[0] = BitConverter.ToUInt16(bytes, 0);
            //    registers[1] = BitConverter.ToUInt16(bytes, 2);
            //}

            byte[] byteArray = BitConverter.GetBytes(value); // 此时byteArray是小端格式

            // 3. 关键：将小端字节序反转，转换为Modbus要求的大端字节序 (ABCD -> DCBA)
            // 或者更准确地说，对于Modbus，是字内字节交换 (AB CD -> BA DC)？ 这里需要确认PLC的配置。
            // 最常见和通用的做法是进行完整的反转 [D, C, B, A]
            //Array.Reverse(byteArray); // 反转后变为大端格式 [42, F6, E9, 79]

            // 4. 将4字节数组转换为2个Ushort（Modbus寄存器是16位的）
            ushort[] registers = new ushort[2];
            registers[0] = BitConverter.ToUInt16(byteArray, 0); // 前两个字节 -> 第一个寄存器
            registers[1] = BitConverter.ToUInt16(byteArray, 2);

            return registers;
           
        }

        /// <summary>
        /// 将数字转成float
        /// </summary>
        /// <param name="value"></param>
        /// <param name="swapRegisters"></param>
        /// <returns></returns>
        public static ushort[] GetInt(int value)
        {
            ushort[] registers = new ushort[2];
            registers[0] = (ushort)value; // 
            registers[1] = 0;

            return registers;

        }

        // 读取单个浮点数
        public static float ReadFloat( ushort startAddress, bool swapRegisters = false)
        {
            // 读取两个寄存器（32位）
            ushort[] registers = ReadHoldingRegisters(startAddress, 2);

            if (swapRegisters)
            {
                // 有些设备需要交换寄存器顺序
                Array.Reverse(registers);
            }

            // 将ushort数组转换为字节数组
            byte[] bytes = new byte[4];
            Buffer.BlockCopy(registers, 0, bytes, 0, 4);

            // 转换为float
            return BitConverter.ToSingle(bytes, 0);
        }

        /// <summary>
        /// 读取单个浮点数
        /// </summary>
        /// <param name="registers">  读取两个寄存器（32位）</param>
        /// <param name="swapRegisters"></param>
        /// <returns></returns>
        public static float ChangeFloat(ushort[] registers, bool swapRegisters = false)
        {
           
         

            if (swapRegisters)
            {
                // 有些设备需要交换寄存器顺序
                Array.Reverse(registers);
            }

            // 将ushort数组转换为字节数组
            byte[] bytes = new byte[4];
            Buffer.BlockCopy(registers, 0, bytes, 0, 4);

            // 转换为float
            return BitConverter.ToSingle(bytes, 0);
        }



    }
}
